package com.yiba.vpn.proxy;


import com.yiba.vpn.core.LocalVpnService;
import com.yiba.vpn.protocal.dns.DnsPacket;
import com.yiba.vpn.protocal.tcpip.IPHeader;
import com.yiba.vpn.protocal.tcpip.UDPHeader;
import com.yiba.vpn.utils.CommonMethods;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 增加UDP的tunnl代理
 * Created by Administrator on 2017/10/27 0027.
 */

public class UdpProxy {
    private static ExecutorService mFixedThreadExecutor = null;
    private DatagramSocket mClient = new DatagramSocket(0);
    public class RequestThread implements Runnable {
        IPHeader mipHeader;
        int msize;
        UDPHeader mudpHeader;

        public RequestThread(IPHeader ipHeader, UDPHeader tcpHeader, int size) {
            this.mipHeader = ipHeader;
            this.mudpHeader = tcpHeader;
            this.msize = size;
        }

        @Override
        public void run() {
            byte[] RECEIVE_BUFFER = new byte[2000];
            IPHeader ipHeader = new IPHeader(RECEIVE_BUFFER, 0);
            ipHeader.Default();
            UDPHeader udpHeader = new UDPHeader(RECEIVE_BUFFER, 20);
            ByteBuffer dnsBuffer = ByteBuffer.wrap(RECEIVE_BUFFER);
            dnsBuffer.position(28);
            dnsBuffer = dnsBuffer.slice();
            DatagramPacket packet = new DatagramPacket(RECEIVE_BUFFER,
                    28, RECEIVE_BUFFER.length - 28);
            try {
                mClient.setSoTimeout(5000);

                while (mClient != null && !mClient.isClosed()) {

                        packet.setLength(RECEIVE_BUFFER.length - 28);
                        mClient.receive(packet);
                        dnsBuffer.clear();
                        dnsBuffer.limit(packet.getLength());
                    try {
                            DnsPacket dnsPacket = DnsPacket.FromBytes(dnsBuffer);
                            if (dnsPacket != null) {
                                OnUDPResponseReceived(mipHeader.getDestinationIP(),mipHeader.getSourceIP(),mudpHeader.getDestinationPort()
                                        ,mudpHeader.getSourcePort(),ipHeader, udpHeader, packet.getLength());
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            LocalVpnService.Instance.writeLog("Parse dns error: %s", e);
                        }
                    }

            } catch (SocketException e2) {
                e2.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

        public UdpProxy() throws SocketException {
            mFixedThreadExecutor = Executors.newFixedThreadPool(30);
        }

        public void onUDPRequestReceivedHandler(IPHeader ipHeader, UDPHeader udpHeader, int size) {
            System.gc();
            mFixedThreadExecutor.execute(new RequestThread(ipHeader, udpHeader, size));
            onUDPRequestReceived(this.mClient, ipHeader, udpHeader, size);
        }

        public void onUDPRequestReceived(DatagramSocket mClient, IPHeader ipHeader, UDPHeader udpHeader, int size) {
            InetSocketAddress remoteAddress = new InetSocketAddress(CommonMethods.ipIntToInet4Address(ipHeader.getDestinationIP()),
                    udpHeader.getDestinationPort()&0xFFFF);
            DatagramPacket packet = new DatagramPacket(udpHeader.m_Data, udpHeader.m_Offset + 8, size);
            packet.setSocketAddress(remoteAddress);
            try {
                if (LocalVpnService.Instance.protect(mClient)) {
                    mClient.send(packet);
                }
            } catch (IOException e) {
                e.printStackTrace(System.err);
            }
        }

        private void OnUDPResponseReceived(int remoteIP, int clientIP, short remotePort, short clientPort,IPHeader ipHeader, UDPHeader udpHeader, int lonlifeLength) {
            ipHeader.setSourceIP(remoteIP);
            ipHeader.setDestinationIP(clientIP);
            ipHeader.setProtocol((byte) 17);
            ipHeader.setTotalLength(lonlifeLength + 28);
            udpHeader.setSourcePort(remotePort);
            udpHeader.setDestinationPort(clientPort);
            udpHeader.setTotalLength(lonlifeLength + 8);
            LocalVpnService.Instance.sendUDPPacket(ipHeader, udpHeader);
        }
}
